﻿using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System;
using System.Threading.Tasks;

namespace DotNetty.Wraper
{
    class TcpSocketClientBuilder : BaseGenericClientBuilder<ITcpSocketClientBuilder, ITcpSocketClient, byte[]>
        , ITcpSocketClientBuilder
    {
        public TcpSocketClientBuilder(ClientOption _option)
            : base(_option)
        {
        }
        //protected Action<IChannelPipeline> _setEncoder { get; set; }

        //public ITcpSocketClientBuilder SetLengthFieldDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, ByteOrder byteOrder = ByteOrder.BigEndian)
        //{
        //    this.AddLast(new LengthFieldBasedFrameDecoder(byteOrder, maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, true));

        //    return this;
        //}

        //public ITcpSocketClientBuilder SetLengthFieldEncoder(int lengthFieldLength)
        //{
        //   this.AddLast(new LengthFieldPrepender(lengthFieldLength));

        //    return this;
        //}

        ///// <summary>
        ///// Inserts multiple <see cref="IChannelHandler"/>s at the last position of this pipeline.
        ///// </summary>
        ///// <param name="handlers"></param>
        ///// <returns></returns>
        //public ITcpSocketClientBuilder AddLast(params IChannelHandler[] handlers)
        //{
        //    _setEncoder += x => x.AddLast(handlers);

        //    return this;
        //}
        ///// <summary>
        ///// Appends an <see cref="Transport.Channels.IChannelHandler"/> at the last position of this pipeline.
        ///// </summary>
        ///// <param name="name"></param>
        ///// <param name="handler"></param>
        ///// <returns></returns>
        //public ITcpSocketClientBuilder AddLast(string name, IChannelHandler handler)
        //{
        //    _setEncoder += x => x.AddLast(name, handler);

        //    return this;
        //}

        public async override Task<ITcpSocketClient> BuildAsync( Action<Transport.Channels.IChannelPipeline> OnPipelineAction = null, Func<ITcpSocketClient, BaseSimpleChannelInboundHandler<object>> addChannelHandler = null)
        {
            //Environment.SetEnvironmentVariable("io.netty.allocator.numDirectArenas", "0");
            //Environment.SetEnvironmentVariable("io.netty.allocator.numHeapArenas", "0");
            if (option == null) throw new ArgumentNullException("option");

            var tcpClient = new TcpSocketClient(option.SeverIp, option.Port, _event);
            string targetHost = null;
            if (!string.IsNullOrWhiteSpace(option.Certificate)&& System.IO.File.Exists(option.Certificate))
            {
               var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(option.Certificate, option.CertificatePassword);
                targetHost = cert.GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType.DnsName, false);
            }
            IEventLoopGroup group;
            if (option.UseLibuv)
            {
                group = new Transport.Libuv.EventLoopGroup();
            }
            else
            {
                group = new MultithreadEventLoopGroup();
            }
            var _bootstrap = new Bootstrap();
            if (option.TcpNodelay)
            {
                _bootstrap.Option(ChannelOption.TcpNodelay, true);
            }
            if (option.SoKeepalive)
            {
                _bootstrap.Option(ChannelOption.SoKeepalive, true);
            }
            if (option.ConnectTimeout > 0)
            {
                _bootstrap.Option(ChannelOption.ConnectTimeout, TimeSpan.FromMilliseconds(option.ConnectTimeout));
            }
            var clientChannel = await _bootstrap
                .Group(group)
                .Channel<TcpSocketChannel>()
                //.Option(ChannelOption.TcpNodelay, true)
                .Handler(new ActionChannelInitializer<IChannel>(channel =>
                {
                    var pipeline = channel.Pipeline;
                    if (!string.IsNullOrWhiteSpace(targetHost))
                    {
                        pipeline.AddLast(new Handlers.Tls.TlsHandler(stream => new System.Net.Security.SslStream(stream, true, (sender, certificate, chain, errors) => true), new Handlers.Tls.ClientTlsSettings(targetHost)));
                    }
                    OnPipelineAction?.Invoke(pipeline);

                    if (addChannelHandler != null)
                        pipeline.AddLast("tcpClient", addChannelHandler(tcpClient));
                    else
                        pipeline.AddLast("tcpClient", new CommonChannelHandler(tcpClient));
                })).ConnectAsync($"{option.SeverIp}:{option.Port}".ToIPEndPoint());

            tcpClient.SetChannel(clientChannel);
            tcpClient.AfterClose = () =>
            {
                group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(option.QuietPeriod), TimeSpan.FromSeconds(option.ShutdownTimeout)).Wait();
            };

            return await Task.FromResult(tcpClient);
        }
    }
}
